/*-------------------------------------------------------------------------
 *
 * datetime.h
 *	  日期/时间支持代码的定义。
 *	  支持代码与其他日期数据类型共享，包括日期和时间。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/datetime.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef DATETIME_H
#define DATETIME_H

#include "nodes/nodes.h"
#include "utils/timestamp.h"

/* 这个结构在 utils/tzparser.h 中声明： */
struct tzEntry;


/* ----------------------------------------------------------------
 *				时间类型 + 支持宏
 *
 * 标准时间量的字符串定义。
 *
 * 这些字符串是用于形成输出时间字符串的默认值。
 * 其他备用形式硬编码在 datetime.c 的令牌表中。
 * ----------------------------------------------------------------
 */

#define DAGO			"ago"
#define DCURRENT		"current"
#define EPOCH			"epoch"
#define INVALID			"invalid"
#define EARLY			"-infinity"
#define LATE			"infinity"
#define NOW				"now"
#define TODAY			"today"
#define TOMORROW		"tomorrow"
#define YESTERDAY		"yesterday"
#define ZULU			"zulu"

#define DMICROSEC		"usecond"
#define DMILLISEC		"msecond"
#define DSECOND			"second"
#define DMINUTE			"minute"
#define DHOUR			"hour"
#define DDAY			"day"
#define DWEEK			"week"
#define DMONTH			"month"
#define DQUARTER		"quarter"
#define DYEAR			"year"
#define DDECADE			"decade"
#define DCENTURY		"century"
#define DMILLENNIUM		"millennium"
#define DA_D			"ad"
#define DB_C			"bc"
#define DTIMEZONE		"timezone"

/*
 * 解析的基本时间字段定义。
 *
 *	上午或下午：am, pm，或24小时制。
 *	千年：公元，公元前
 */

#define AM		0
#define PM		1
#define HR24	2

#define AD		0
#define BC		1

/*
 * 时间解码的字段类型。
 *
 * 不能拥有超过无符号整数位数的类型，
 * 因为这些在解析和解码过程中被转换为位掩码。
 *
 * 此外，YEAR、MONTH、DAY、HOUR、MINUTE、SECOND 的值
 * 必须在 0..14 范围内，以便相关的位掩码可以适应
 * INTERVAL 的 typmod 值的左半部分。由于这些位
 * 存储在 typmods 中，你不能在不执行 initdb 的情况下更改它们！
 */

#define RESERV	0
#define MONTH	1
#define YEAR	2
#define DAY		3
#define JULIAN	4
#define TZ		5				/* 固定偏移时区缩写 */
#define DTZ		6				/* 固定偏移时区缩写，夏令时 */
#define DYNTZ	7				/* 动态时区缩写 */
#define IGNORE_DTF	8
#define AMPM	9
#define HOUR	10
#define MINUTE	11
#define SECOND	12
#define MILLISECOND 13
#define MICROSECOND 14
#define DOY		15
#define DOW		16
#define UNITS	17
#define ADBC	18
/* 这些仅适用于相对日期 */
#define AGO		19
#define ABS_BEFORE		20
#define ABS_AFTER		21
/* 通用字段以帮助解析 */
#define ISODATE 22
#define ISOTIME 23
/* 这些仅用于解析间隔 */
#define WEEK		24
#define DECADE		25
#define CENTURY		26
#define MILLENNIUM	27
/* 解析两个单词的时区规范的hack "MET DST"等 */
#define DTZMOD	28				/* "DST"作为一个单独的词 */
/* 保留给未识别的字符串值 */
#define UNKNOWN_FIELD	31

/*
 * 用于时间解析和解码的标记字段定义。
 *
 * 一些字段类型代码（见上文）将这些作为datetktbl[]中的“值”。
 * 这些也用于DecodeDateTime及其相关函数的位掩码
 * 因此实际限制它们在[0,31]之内。
 * - thomas 97/06/19
 * 这些字段并不全用于DecodeDateTime的掩码
 * 因此允许一些大于31的值。- thomas 1997-11-17
 *
 * 警告：代码中有未记录的假设，大多数这些
 * 值不等于IGNORE_DTF或RESERV。在重新编号时
 * 一定要非常小心这些看似独立的列表中的值 :-(
 */

#define DTK_NUMBER		0
#define DTK_STRING		1

#define DTK_DATE		2
#define DTK_TIME		3
#define DTK_TZ			4
#define DTK_AGO			5

#define DTK_SPECIAL		6
#define DTK_EARLY		9
#define DTK_LATE		10
#define DTK_EPOCH		11
#define DTK_NOW			12
#define DTK_YESTERDAY	13
#define DTK_TODAY		14
#define DTK_TOMORROW	15
#define DTK_ZULU		16

#define DTK_DELTA		17
#define DTK_SECOND		18
#define DTK_MINUTE		19
#define DTK_HOUR		20
#define DTK_DAY			21
#define DTK_WEEK		22
#define DTK_MONTH		23
#define DTK_QUARTER		24
#define DTK_YEAR		25
#define DTK_DECADE		26
#define DTK_CENTURY		27
#define DTK_MILLENNIUM	28
#define DTK_MILLISEC	29
#define DTK_MICROSEC	30
#define DTK_JULIAN		31

#define DTK_DOW			32
#define DTK_DOY			33
#define DTK_TZ_HOUR		34
#define DTK_TZ_MINUTE	35
#define DTK_ISOYEAR		36
#define DTK_ISODOW		37


/*
 * 时间解析的位掩码定义。
 */

#define DTK_M(t)		(0x01 << (t))

/* 方便：一秒，加上任何小数部分 */
#define DTK_ALL_SECS_M	(DTK_M(SECOND) | DTK_M(MILLISECOND) | DTK_M(MICROSECOND))
#define DTK_DATE_M		(DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M		(DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M)

/*
 * 用于输入和输出间隔、时间戳等的工作缓冲区大小。
 * 需要更多工作空间的输入将被提前拒绝。更长的输出
 * 将会溢出缓冲区，因此这必须对所有可能的输出都足够。
 * 根据此文的写作，interval_out() 需要最多约 90 字节的空间。
 */
#define MAXDATELEN		128
/* 日期字符串中可能的最大字段数 */
#define MAXDATEFIELDS	25
/* 仅存储这么多字符在datetktbl中 */
#define TOKMAXLEN		10

/* 保持这个结构小；它使用很多 */
typedef struct
{
	char		token[TOKMAXLEN + 1];	/* 始终以NUL结尾 */
	char		type;			/* 见上面的字段类型代码 */
	int32		value;			/* 含义取决于类型 */
} datetkn;

/* 其用途之一是在时区缩写的表中 */
typedef struct TimeZoneAbbrevTable
{
	Size		tblsize;		/* TimeZoneAbbrevTable 的字节大小 */
	int			numabbrevs;		/* entries数组中条目的数量 */
	datetkn		abbrevs[FLEXIBLE_ARRAY_MEMBER];
	/* DynamicZoneAbbrev(s)可能跟随abbrevs[]数组 */
} TimeZoneAbbrevTable;

/* 动态时区缩写的辅助数据（非固定偏移） */
typedef struct DynamicZoneAbbrev
{
	pg_tz	   *tz;				/* 如果尚未查找则为NULL */
	char		zone[FLEXIBLE_ARRAY_MEMBER];	/* NUL终止的时区名称 */
} DynamicZoneAbbrev;


/* FMODULO()
 * 替换modf()的宏，在某些平台上该函数存在问题。
 * t = 输入和余数
 * q = 整数部分
 * u = 除数
 */
#define FMODULO(t,q,u) \
do { \
	(q) = (((t) < 0) ? ceil((t) / (u)) : floor((t) / (u))); \
	if ((q) != 0) (t) -= rint((q) * (u)); \
} while(0)

/* TMODULO()
 * 像FMODULO()，但适用于时间戳数据类型（现在始终为int64）。
 * 我们假设int64遵循C99的除法语义（负商截断为零）。
 */
#define TMODULO(t,q,u) \
do { \
	(q) = ((t) / (u)); \
	if ((q) != 0) (t) -= ((q) * (u)); \
} while(0)

/*
 * 日期/时间验证
 * 包括闰年检查。
 */

extern PGDLLIMPORT const char *const months[];	/* 月（3个字符的缩写） */
extern PGDLLIMPORT const char *const days[];	/* 天（完整名称） */
extern PGDLLIMPORT const int day_tab[2][13];

/*
 * 这些是公历的规则，公历于1582年被采用。
 * 然而，我们对所有之前的年份也使用此计算，因为
 * SQL标准指定使用公历。这防止了
 * 存储日期1500-02-29，尽管它在儒略历中是有效的。
 */
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))


/*
 * Datetime输入解析例程（ParseDateTime，DecodeDateTime等）
 * 成功时返回零或正值。在失败时，他们返回
 * 这些负码值之一。可以使用DateTimeParseError来
 * 生成正确的ereport。
 */
#define DTERR_BAD_FORMAT		(-1)
#define DTERR_FIELD_OVERFLOW	(-2)
#define DTERR_MD_FIELD_OVERFLOW (-3)	/* 触发关于日期样式的提示 */
#define DTERR_INTERVAL_OVERFLOW (-4)
#define DTERR_TZDISP_OVERFLOW	(-5)


extern void GetCurrentDateTime(struct pg_tm *tm);
extern void GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp);
extern void j2date(int jd, int *year, int *month, int *day);
extern int	date2j(int year, int month, int day);

extern int	ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
						  char **field, int *ftype,
						  int maxfields, int *numfields);
extern int	DecodeDateTime(char **field, int *ftype,
						   int nf, int *dtype,
						   struct pg_tm *tm, fsec_t *fsec, int *tzp);
extern int	DecodeTimezone(char *str, int *tzp);
extern int	DecodeTimeOnly(char **field, int *ftype,
						   int nf, int *dtype,
						   struct pg_tm *tm, fsec_t *fsec, int *tzp);
extern int	DecodeInterval(char **field, int *ftype, int nf, int range,
						   int *dtype, struct pg_itm_in *itm_in);
extern int	DecodeISO8601Interval(char *str,
								  int *dtype, struct pg_itm_in *itm_in);

extern void DateTimeParseError(int dterr, const char *str,
							   const char *datatype) pg_attribute_noreturn();

extern int	DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp);
extern int	DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp);
extern int	DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
											pg_tz *tzp, int *isdst);

extern void EncodeDateOnly(struct pg_tm *tm, int style, char *str);
extern void EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str);
extern void EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str);
extern void EncodeInterval(struct pg_itm *itm, int style, char *str);
extern void EncodeSpecialTimestamp(Timestamp dt, char *str);

extern int	ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
						 struct pg_tm *tm);

extern int	DecodeTimezoneAbbrev(int field, char *lowtoken,
								 int *offset, pg_tz **tz);
extern int	DecodeSpecial(int field, char *lowtoken, int *val);
extern int	DecodeUnits(int field, char *lowtoken, int *val);

extern int	j2day(int jd);

extern Node *TemporalSimplify(int32 max_precis, Node *node);

extern bool CheckDateTokenTables(void);

extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
												   int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);

extern void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
extern bool AdjustTimestampForTypmodError(Timestamp *time, int32 typmod,
										  bool *error);

#endif							/* DATETIME_H */
